package edu.iu.criminfo.voronoi.quadtree;

import java.util.List;
import java.util.ArrayList;

import edu.iu.criminfo.voronoi.cases.Case;
import edu.iu.criminfo.voronoi.object.Point;

public class Quadtree {
	
	private Point bottomLeft;
	private Point upperRight;
	
	private List<Case> caseList;
	
	private Quadtree nwChild;
	private Quadtree neChild;
	private Quadtree swChild;
	private Quadtree seChild;

	public Quadtree(Point bottomLeft, Point upperRight) {
		this.bottomLeft = bottomLeft;
		this.upperRight = upperRight;
	}
	
	public Quadtree(Point bottomLeft, Point upperRight, List<Case> caseList) {
		this(bottomLeft, upperRight);
		this.caseList = caseList;
	}
	
	public int numOfCases() {
		return caseList.size();
	}
	
	public void generateTree(int threshold) {
		if (numOfCases() > threshold) {
			expand();
			nwChild.generateTree(threshold);
			neChild.generateTree(threshold);
			swChild.generateTree(threshold);
			seChild.generateTree(threshold);
		}
	}
	
	/*   -------------------
	 *   |   1    |    2   |
	 *   |        |        |
	 *   |--------|--------|
	 *   |   3    |    4   |
	 *   |        |        |
	 *   -------------------
	 */
	public void expand() {
		/* local variable */
		Point lower;
		Point upper;
		List<Case> localCaseList;
	
		/*1. North West */
		lower = new Point(bottomLeft.coordinateX, (bottomLeft.coordinateY + upperRight.coordinateY) / 2);
		upper = new Point((bottomLeft.coordinateX + upperRight.coordinateX) / 2, upperRight.coordinateY);
		localCaseList = new ArrayList<Case>();
		for (Case aCase: caseList) {
			if (aCase.inBound(lower, upper) == true) {
				localCaseList.add(aCase);
			}
		}
		nwChild = new Quadtree(lower, upper, localCaseList);
		
		/*2. North East */
		lower = new Point((bottomLeft.coordinateX + upperRight.coordinateX) / 2, (bottomLeft.coordinateY + upperRight.coordinateY) / 2);
		upper = new Point(upperRight.coordinateX, upperRight.coordinateY);
		localCaseList = new ArrayList<Case>();
		for (Case aCase: caseList) {
			if (aCase.inBound(lower, upper) == true) {
				localCaseList.add(aCase);
			}
		}
		neChild = new Quadtree(lower, upper, localCaseList);
		
		/*3. South West */
		lower = new Point(bottomLeft.coordinateX, bottomLeft.coordinateY);
		upper = new Point((bottomLeft.coordinateX + upperRight.coordinateX) / 2, (bottomLeft.coordinateY + upperRight.coordinateY) / 2);
		localCaseList = new ArrayList<Case>();
		for (Case aCase: caseList) {
			if (aCase.inBound(lower, upper) == true) {
				localCaseList.add(aCase);
			}
		}
		swChild = new Quadtree(lower, upper, localCaseList);
		
		/*4. South East */
		lower = new Point((bottomLeft.coordinateX + upperRight.coordinateX) / 2, bottomLeft.coordinateY);
		upper = new Point(upperRight.coordinateX, (bottomLeft.coordinateY + upperRight.coordinateY) / 2);
		localCaseList = new ArrayList<Case>();
		for (Case aCase: caseList) {
			if (aCase.inBound(lower, upper) == true) {
				localCaseList.add(aCase);
			}
		}
		seChild = new Quadtree(lower, upper, localCaseList);
	}
	
	public void traverseTree() {
		if ((nwChild==null)||(neChild==null)||(swChild==null)||(seChild==null)) {
			System.out.println("i'm leaf and I contain: " + caseList.size() + " nodes");
		}
		else {
			nwChild.traverseTree();
			neChild.traverseTree();
			swChild.traverseTree();
			seChild.traverseTree();
		}
	}

	public void setCaseList(List<Case> caseList) {
		this.caseList = caseList;
	}

	public List<Case> getCaseList() {
		return caseList;
	}	

	public Quadtree getNwChild() {
		return nwChild;
	}

	public Quadtree getNeChild() {
		return neChild;
	}	

	public Quadtree getSwChild() {
		return swChild;
	}

	public Quadtree getSeChild() {
		return seChild;
	}
	
}
